<script setup>
import { computed, ref } from 'vue'
import { marked } from 'marked'
import DOMPurify from 'dompurify';
import hljs from 'highlight.js'
import 'highlight.js/styles/github.css'

// 配置 marked
marked.setOptions({
  highlight: function (code, lang) {
    if (lang && hljs.getLanguage(lang)) {
      return hljs.highlight(code, { language: lang }).value
    }
    return hljs.highlightAuto(code).value
  },
  breaks: true
})

const props = defineProps({
  type: {
    type: String,
    required: true,
    validator: (value) => ['user', 'assistant'].includes(value)
  },
  content: {
    type: String,
    required: true
  },
  timestamp: {
    type: String,
    default: null
  },
  isStreaming: {
    type: Boolean,
    default: false
  },
  isError: {
    type: Boolean,
    default: false
  },
  sources: {
    type: Array,
    default: () => []
  }
});

const avatarClass = computed(() => {
  return props.type === 'assistant' 
    ? 'bg-blue-500' 
    : 'bg-green-500';
});

const messageClass = computed(() => {
  if (props.isError) {
    return 'bg-red-50 border-red-100';
  }
  return props.type === 'assistant' 
    ? 'bg-blue-50 border-blue-100'
    : 'bg-gray-50 border-gray-100'
})

// 检查内容是否为图片
const isImage = computed(() => {
  const content = props.content.trim();
  // 判断是否为Markdown图片语法
  if (content.startsWith('![') && content.includes('](') && content.endsWith(')')) {
    return true;
  }
  // 判断是否为HTML img标签
  if (content.startsWith('<img ') && content.includes('src="') && content.endsWith('>')) {
    return true;
  }
  // 判断是否为纯图片URL (支持常见图片格式)
  const imageExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.webp'];
  if (imageExtensions.some(ext => content.toLowerCase().endsWith(ext))) {
    // 检查是否为完整的URL或数据URL
    return content.startsWith('http') || content.startsWith('data:image');
  }
  return false;
});

// 提取图片URL
const imageUrl = computed(() => {
  if (!isImage.value) return null;
  
  const content = props.content.trim();
  
  // 从Markdown格式提取
  if (content.startsWith('![') && content.includes('](') && content.endsWith(')')) {
    const match = content.match(/!\[.*?\]\((.*?)\)/);
    return match ? match[1] : null;
  }
  
  // 从HTML img标签提取
  if (content.startsWith('<img ') && content.includes('src="')) {
    const match = content.match(/src="([^"]+)"/);
    return match ? match[1] : null;
  }
  
  // 纯URL
  return content;
});

// 检查内容是否为音频
const isAudio = computed(() => {
  const content = props.content.trim();
  
  // 判断是否为Markdown音频语法
  if (content.startsWith('[audio](') && content.endsWith(')')) {
    return true;
  }
  
  // 判断是否为HTML audio标签
  if (content.includes('<audio ') && content.includes('</audio>')) {
    return true;
  }
  
  // 判断是否为纯音频URL (支持常见音频格式)
  const audioExtensions = ['.mp3', '.wav', '.ogg', '.m4a', '.aac'];
  if (audioExtensions.some(ext => content.toLowerCase().endsWith(ext))) {
    return content.startsWith('http') || content.startsWith('data:audio');
  }
  
  return false;
});

// 提取音频URL
const audioUrl = computed(() => {
  if (!isAudio.value) return null;
  
  const content = props.content.trim();
  
  // 从Markdown格式提取
  if (content.startsWith('[audio](') && content.endsWith(')')) {
    const match = content.match(/\[audio\]\((.*?)\)/);
    return match ? match[1] : null;
  }
  
  // 从HTML audio标签提取
  if (content.includes('<audio ') && content.includes('src="')) {
    const match = content.match(/src="([^"]+)"/);
    return match ? match[1] : null;
  }
  
  // 纯URL
  return content;
});

// 格式化内容，将Markdown转换为HTML
const formattedContent = computed(() => {
  if (!props.content) return '';
  
  // 如果是图片，直接返回图片标签
  if (isImage.value && imageUrl.value) {
    return `<img src="${DOMPurify.sanitize(imageUrl.value)}" alt="图片内容" class="max-w-full rounded">`;
  }
  
  // 如果是音频，返回音频播放器
  if (isAudio.value && audioUrl.value) {
    return `<audio controls class="w-full"><source src="${DOMPurify.sanitize(audioUrl.value)}" type="audio/mpeg">您的浏览器不支持音频播放器</audio>`;
  }
  
  try {
    // 使用marked转换Markdown
    const rawHtml = marked(props.content);
    
    // 使用DOMPurify清理HTML，防止XSS攻击
    const cleanHtml = DOMPurify.sanitize(rawHtml);
    return cleanHtml;
  } catch (error) {
    console.error('Error formatting message content:', error);
    return props.content;
  }
});

// 显示来源
const showSources = ref(false);
const toggleSources = () => {
  showSources.value = !showSources.value;
};

// 检查是否有来源
const hasSources = computed(() => {
  return props.sources && props.sources.length > 0;
});
</script>

<template>
  <div class="mb-6">
    <div class="flex items-start gap-4">
      <!-- 头像 -->
      <div class="flex-shrink-0">
        <div :class="[
          'w-12 h-12 rounded-xl shadow-lg flex items-center justify-center text-white',
          avatarClass
        ]">
          <i :class="[
            'text-xl',
            type === 'assistant' ? 'fas fa-robot' : 'fas fa-user'
          ]"></i>
        </div>
      </div>

      <!-- 消息内容 -->
      <div class="flex-grow overflow-hidden">
        <div class="flex items-center gap-3 mb-2">
          <span class="font-semibold" :class="type === 'assistant' ? 'text-blue-700' : 'text-emerald-700'">
            {{ type === 'assistant' ? '智能助手' : '你' }}
          </span>
          <span v-if="timestamp" class="text-sm text-gray-400">{{ new Date(timestamp).toLocaleString() }}</span>
          
          <!-- 流式输出指示器 -->
          <span v-if="isStreaming" class="inline-flex items-center text-sm text-blue-500">
            <span class="typing-indicator">正在思考</span>
            <span class="typing-dots ml-1">
              <span class="dot"></span>
              <span class="dot"></span>
              <span class="dot"></span>
            </span>
          </span>
          
          <!-- 知识库来源按钮 -->
          <button 
            v-if="hasSources && type === 'assistant' && !isStreaming" 
            @click="toggleSources"
            class="text-xs px-2 py-1 rounded-md bg-blue-100 text-blue-700 hover:bg-blue-200 transition-colors"
          >
            {{ showSources ? '隐藏来源' : '查看来源' }}
          </button>
        </div>
        
        <!-- 消息内容 -->
        <div 
          :class="[
            'prose prose-sm p-4 rounded-xl border shadow-sm overflow-x-auto',
            messageClass,
            {'typing-effect': isStreaming && type === 'assistant'}
          ]"
          v-html="formattedContent"
        ></div>
        
        <!-- 知识库来源 -->
        <div v-if="showSources && hasSources" class="mt-2 text-sm">
          <div class="p-3 bg-gray-50 rounded-lg border border-gray-200">
            <h4 class="font-medium text-gray-700 mb-2">参考来源</h4>
            <div v-for="(source, index) in sources" :key="index" class="mb-3 last:mb-0">
              <div class="p-2 bg-white rounded border border-gray-100">
                <div class="text-xs text-gray-500 mb-1">
                  来源: {{ source.metadata?.source || '未知' }}
                </div>
                <div class="text-gray-700">{{ source.content }}</div>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
  </div>
</template>

<style>
.prose {
  @apply text-gray-700;
}

.prose pre {
  @apply bg-white border border-gray-100 rounded-lg p-4 shadow-sm;
}

.prose code {
  @apply text-blue-600 bg-blue-50 px-2 py-0.5 rounded-md;
}

.prose pre code {
  @apply text-gray-800 bg-transparent p-0;
}

.prose a {
  @apply text-blue-600 no-underline hover:underline decoration-2 decoration-blue-300;
}

.prose ul {
  @apply list-disc pl-6 my-2 marker:text-blue-500;
}

.prose ol {
  @apply list-decimal pl-6 my-2 marker:text-blue-500;
}

.prose p {
  @apply my-2;
}

.prose h1,
.prose h2,
.prose h3,
.prose h4,
.prose h5,
.prose h6 {
  @apply font-bold text-gray-900 my-4;
}

.prose h1 {
  @apply text-2xl;
}

.prose h2 {
  @apply text-xl;
}

.prose h3 {
  @apply text-lg;
}

.prose blockquote {
  @apply border-l-4 border-blue-200 pl-4 italic my-4 text-gray-600 bg-blue-50 py-2 rounded-r-lg;
}

.prose hr {
  @apply my-6 border-gray-200;
}

.prose table {
  @apply w-full border-collapse my-4 bg-white rounded-lg overflow-hidden shadow-sm;
}

.prose th {
  @apply bg-gray-50 text-gray-700 font-semibold px-4 py-2 border-b border-gray-200;
}

.prose td {
  @apply px-4 py-2 border-b border-gray-100;
}

.prose tr:last-child td {
  @apply border-b-0;
}

.prose tr:hover {
  @apply bg-gray-50;
}

/* 图片和音频样式 */
.prose img {
  @apply max-w-full h-auto rounded-lg my-4 shadow-sm;
}

.prose audio {
  @apply w-full my-4;
}

/* 代码高亮主题 */
.hljs {
  @apply bg-transparent;
}

.hljs-keyword,
.hljs-tag {
  @apply text-purple-600;
}

.hljs-string {
  @apply text-green-600;
}

.hljs-number {
  @apply text-orange-600;
}

.hljs-comment {
  @apply text-gray-500 italic;
}

.hljs-function {
  @apply text-blue-600;
}

/* 增强流式输出相关样式 */
.typing-indicator {
  display: inline-block;
}

.typing-dots {
  display: inline-flex;
  align-items: center;
}

.typing-dots .dot {
  display: inline-block;
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background-color: currentColor;
  margin: 0 2px;
  animation: typing-dot 1.4s infinite ease-in-out both;
}

.typing-dots .dot:nth-child(1) {
  animation-delay: -0.32s;
}

.typing-dots .dot:nth-child(2) {
  animation-delay: -0.16s;
}

@keyframes typing-dot {
  0%, 80%, 100% {
    transform: scale(0);
    opacity: 0.5;
  }
  40% {
    transform: scale(1);
    opacity: 1;
  }
}

.typing-effect {
  border-color: #3b82f6 !important; 
  box-shadow: 0 0 0 1px rgba(59, 130, 246, 0.3) !important;
  position: relative;
}

.typing-effect::after {
  content: '|';
  display: inline-block;
  animation: cursor-blink 0.8s infinite;
  margin-left: 2px;
  font-weight: bold;
  color: #3b82f6;
  font-size: 1.2em;
  line-height: 1;
  vertical-align: middle;
}

@keyframes cursor-blink {
  0%, 100% {
    opacity: 1;
  }
  50% {
    opacity: 0;
  }
}

/* 为流式输出添加动画效果 */
@keyframes highlight-text {
  0% {
    background-color: rgba(59, 130, 246, 0.1);
  }
  100% {
    background-color: transparent;
  }
}

.typing-effect p:last-child,
.typing-effect li:last-child,
.typing-effect code:last-child {
  animation: highlight-text 1.5s ease-out;
}
</style> 